using System;
using System.Text;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
using System.Web.UI.Design;
using System.Collections.Generic;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;

[assembly: WebResource("Ajax.Logging.Scripts.ExceptionManager.js", "text/javascript")]


namespace Ajax.Logging
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [Designer(typeof(ExceptionManagerDesigner))]
    [ToolboxData("<{0}:ExceptionManager runat=\"server\"></{0}:ExceptionManager>")]
    [ParseChildren(true, "Listeners")]
    [PersistChildren(false)]
    [DefaultProperty("HandleUnhandledException")]
    public class ExceptionManager : Control, IScriptControl
    {
        private const int DefaultUnhandledExceptionCode = (System.Web.Management.WebEventCodes.WebExtendedBase + 5000);
        private const int DefaultUpdatePanelExceptionCode = (DefaultUnhandledExceptionCode + 1);

        private ListenerCollection _listeners;

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Visible
        {
            [DebuggerStepThrough()]
            get
            {
                return base.Visible;
            }
            [DebuggerStepThrough()]
            set
            {
                throw new NotImplementedException();
            }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        [Description("Whether to handle the unhandled errors.")]
        public bool HandleUnhandledException
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["handleUnhandledException"];

                return (obj == null) ? true : (bool)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["handleUnhandledException"] = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(DefaultUnhandledExceptionCode)]
        [Description("Unhandled errors error code.")]
        public int UnhandledExceptionCode
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["unhandledExceptionCode"];

                return (obj == null) ? DefaultUnhandledExceptionCode : (int)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["unhandledExceptionCode"] = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Whether to show the unhandled error alert box, it does not make any effect if it is not set to handle the unhandled errors.")]
        public bool ShowUnhandledException
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["showUnhandledException"];

                return (obj == null) ? false : (bool)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["showUnhandledException"] = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(true)]
        [Description("Whether to handle the update panel errors.")]
        public bool HandleUpdatePanelException
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["handleUpdatePanelException"];

                return (obj == null) ? true : (bool)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["handleUpdatePanelException"] = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(DefaultUpdatePanelExceptionCode)]
        [Description("Update Panels error code.")]
        public int UpdatePanelExceptionCode
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["updatePanelExceptionCode"];

                return (obj == null) ? DefaultUpdatePanelExceptionCode : (int)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["updatePanelExceptionCode"] = value;
            }
        }

        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Whether to show the update panel exception, it does not make any effect if the page does not have any update panel or it is not set to handle the update panel errors.")]
        public bool ShowUpdatePanelException
        {
            [DebuggerStepThrough()]
            get
            {
                object obj = ViewState["showUpdatePanelException"];

                return (obj == null) ? false : (bool)obj;
            }
            [DebuggerStepThrough()]
            set
            {
                ViewState["showUpdatePanelException"] = value;
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [DefaultValue(null)]
        [Browsable(false)]
        public ListenerCollection Listeners
        {
            [DebuggerStepThrough()]
            get
            {
                if (_listeners == null)
                {
                    _listeners = new ListenerCollection(this);
                }

                return _listeners;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
        protected override void OnPreRender(EventArgs e)
        {
            ScriptManager sm = ScriptManager.GetCurrent(this.Page);

            if (sm == null)
            {
                throw new InvalidOperationException("ScriptManager required on the page.");
            }

            sm.RegisterScriptControl(this);

            GenerateUnhandledErrorScript();
            GenerateListenersCreateScript();
            GenerateUpdatePanelErrorScript();

            base.OnPreRender(e);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!DesignMode)
            {
                ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);
            }

            base.Render(writer);
        }

        public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            yield break;
        }

        public IEnumerable<ScriptReference> GetScriptReferences()
        {
            List<ScriptReference> scripts = new List<ScriptReference>();

            scripts.Add(new ScriptReference(Page.ClientScript.GetWebResourceUrl(this.GetType(), "Ajax.Logging.Scripts.ExceptionManager.js")));

            foreach (BaseListener listener in Listeners)
            {
                scripts.AddRange(listener.ScriptReferences);
            }

            return scripts.ToArray();
        }

        private void GenerateUnhandledErrorScript()
        {
            if (HandleUnhandledException)
            {
                string script = "window.onerror = function(message, url, lineNumber)\n" +
                                "{\n" +
                                    "\tvar e = Error.create(message, {description:message, name:'UnhandledError', lineNumber:lineNumber, url:url});\n" +
                                    "\tAjax.Logging.ExceptionManager.getInstance().publishException(" + UnhandledExceptionCode.ToString(CultureInfo.CurrentCulture) + ", e);\n" +
                                    "\treturn " + (!ShowUnhandledException).ToString().ToLowerInvariant() + ";\n" +
                                "}\n";

                ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "unhandledError", script, true);
            }
        }

        private void GenerateListenersCreateScript()
        {
            StringBuilder script = new StringBuilder();

            script.Append("Sys.Application.add_init(function(){");

            foreach (BaseListener listener in Listeners)
            {
                script.AppendFormat(CultureInfo.CurrentCulture, "\n{0}", listener.CreateScript);
            }

            script.Append("\n});\n");

            if (script.Length > 0)
            {
                ScriptManager.RegisterStartupScript(this, this.GetType(), "listeners", script.ToString(), true);
            }
        }

        private void GenerateUpdatePanelErrorScript()
        {
            if (HandleUpdatePanelException)
            {
                if (ScriptManager.GetCurrent(this.Page).EnablePartialRendering)
                {
                    string script = "Sys.WebForms.PageRequestManager.getInstance().add_endRequest(AjaxLoggingHandleUpdatePanelError)\n" +
                                    "function AjaxLoggingHandleUpdatePanelError(sender, args)\n" +
                                    "{\n" +
                                    "\tvar e = args.get_error();\n" +
                                    "\tif (e != null)\n" +
                                    "\t{\n" +
                                    "\t\tAjax.Logging.ExceptionManager.getInstance().publishException(" + UpdatePanelExceptionCode.ToString(CultureInfo.CurrentCulture) + ", e);\n" +
                                    "\t\targs.set_errorHandled(" + (!ShowUpdatePanelException).ToString(CultureInfo.InvariantCulture).ToLower(CultureInfo.InvariantCulture) + ");\n" +
                                    "\t}\n" +
                                    "}\n";

                    ScriptManager.RegisterStartupScript(this, this.GetType(), "updatePanelError", script, true);
                }
            }
        }
    }

    internal sealed class ExceptionManagerDesigner : ControlDesigner
    {
        private const string PlaceHolderDesignTimeHtmlTemplate = "<table cellpadding=4 cellspacing=0 style=\"font:messagebox;color:buttontext;background-color:buttonface;border: solid 1px;border-top-color:buttonhighlight;border-left-color:buttonhighlight;border-bottom-color:buttonshadow;border-right-color:buttonshadow\">\r\n              <tr><td nowrap><span style=\"font-weight:bold\">{0}</span> - {1}</td></tr>\r\n              <tr><td>{2}</td></tr>\r\n            </table>";

        public override string GetDesignTimeHtml()
        {
            string name = base.Component.GetType().Name;
            string site = base.Component.Site.Name;

            return string.Format(CultureInfo.InvariantCulture, PlaceHolderDesignTimeHtmlTemplate, name, site, string.Empty);
        }
    }
}